<template>
  <view class="z-upload">
    <!-- 预览区 -->
    <template v-if="!customPreview">
      <view v-for="(item, index) in files" :key="index" class="upload-preview_item"
        :style="{ width: `${width}rpx`, height: `${height}rpx` }" @click="onPreview(index)">
        <image v-if="isFile" src="/static/images/common/ic_file_download.png"
          :style="{ width: `${width}rpx`, height: `${height}rpx` }" mode="aspectFill" />
        <image v-else :src="item.url" :style="{ width: `${width}rpx`, height: `${height}rpx` }" mode="aspectFill" />
        <view v-if="deletable" class="upload-preview_item-delete" @click.stop="onDeleteClick(index)">
          <u-icon name="close" size="20" color="#fff" />
        </view>
        <view v-if="item.progress != 100" class="uploading">
          <u-line-progress :percent="item.progress" :show-percent="false" height="18" active-color="#4A71F0" />
        </view>
      </view>
    </template>
    <slot v-else-if="files && files.length > 0" name="preview" :fileList="files" />
    <!-- 上传按钮 -->
    <view v-if="!isUpToMax" @click="onAddClick">
      <view v-if="!$slots.addBtn" class="upload-btn_add" :style="{ width: `${width}rpx`, height: `${height}rpx` }">
        <u-icon name="plus" color="#ccc" size="90" />
      </view>
      <slot v-else name="addBtn" />
    </view>
  </view>
</template>

<script setup>
import { showModal } from '@/common/util/uni'
import { uploadUrl } from '@/common/config/config'
import { useUserStore } from '@/store/user.js'
import { isImageUrl } from '@/common/util/util'

const userStore = useUserStore()

const emit = defineEmits(['delete', 'add', 'chooseSuccess', 'chooseFail', 'uploadSuccess', 'uploadFail'])
const props = defineProps({
  // 上传地址
  url: {
    type: String,
    default: uploadUrl,
  },
  header: {
    type: Object,
  },
  // 宽度
  width: {
    type: [Number, String],
  },
  // 高度
  height: {
    type: [Number, String],
  },
  // 最大上传数量
  maxCount: {
    type: [Number, String],
  },
  // 是否多选
  multipleSelect: {
    type: Boolean,
    default: true,
  },
  // 是否展示删除按钮
  deletable: {
    type: Boolean,
    default: true,
  },
  // 显示已上传的文件列表
  fileList: {
    type: [Array, String],
    default: () => [],
  },
  fileType: {
    type: [Array, String],
    default: () => ['image'],
  },
  // 上传的文件字段名
  uploadName: {
    type: String,
    default: 'file',
  },
  // 所选的图片的尺寸, 可选值为original compressed
  sizeType: {
    type: Array,
    default: () => ['original', 'compressed'],
  },
  // 是否自动上传
  autoUpload: {
    type: Boolean,
    default: true,
  },
  // 自定义显示
  customPreview: {
    type: Boolean,
    default: false,
  },
  
})

const files = ref(props.fileList ? JSON.parse(JSON.stringify(props.fileList.map(item => serverFileMap(item)))) : [])
const state = reactive({
  uploading: false,
  url: ''
})

const fileTypes = computed(() => {
  const fileTypeType = typeof props.fileType
  let finalFileType = []
  if (fileTypeType === 'string') {
    finalFileType.push(props.fileType)
  } else if (fileTypeType === 'object') {
    finalFileType = props.fileType
  }
  return finalFileType
})

const isFile = computed(() => fileTypes.value.indexOf('file') != -1)

const defHeader = {
  token: userStore.token
}

watch(
  () => props.fileList,
  value => {
    files.value = value.map(item => serverFileMap(item))
  }
)

const isUpToMax = computed(() => files.value.length >= Number(props.maxCount))

function serverFileMap(serverData) {
  return {
    url: serverData.url,
    serverUrl: serverData.url,
    name: serverData.name,
    progress: 100,
    error: false,
  }
}

function onPreview(index) {
  if (isFile.value) {
    const file = files.value[index]
    const url = file.url || file.serverUrl
    if (isImageUrl(url)) {
      uni.previewImage({
        urls: [url],
        current: 0,
      })
    } else {
      if (file.url && file.serverUrl) {
        uni.openDocument({
          filePath: file.url,
        })
      } else {
        uni.showLoading({
          title: '正在下载',
        })
        uni.downloadFile({
          url: files.value[index].serverUrl || files.value[index].url,
          success: res => {
            uni.hideLoading()
            uni.openDocument({
              filePath: res.tempFilePath,
            })
          },
        })
      }
    }
  } else {
    uni.previewImage({
      urls: files.value.map(item => item.url),
      current: index,
    })
  }
}

function onAddClick() {
  const maxCount = props.maxCount - files.value.length

  const isMedia = fileTypes.value.indexOf('image') != -1
  if (isMedia) {
    uni.chooseImage({
      count: props.multipleSelect ? (maxCount > 9 ? 9 : maxCount) : 1,
      sizeType: props.sizeType,
      success: res => {
        onFileChoosed(res)
      },
      fail: err => emit('chooseFail', err),
    })
  } else {
    wx.chooseMessageFile({
      type: 'file',
      success: res => onFileChoosed(res),
      fail: err => emit('chooseFail', err),
    })
  }
}

function onFileChoosed(res) {
  if (!props.multipleSelect && res.tempFiles.length > 1) return
  emit('chooseSuccess', res.tempFiles.map(item => {
    return { url: item.tempFilePath || item.path }
  }))
  const tempFiles = res.tempFiles.map(item => {
    return {
      url: item.tempFilePath || item.path,
      name: item.name,
      progress: 0,
      error: false,
    }
  })

  const index = files.value.length
  files.value = files.value.concat(tempFiles)

  setTimeout(() => {
    if (props.autoUpload) {
      uploadFile(files.value, index)
    }
  }, 300)
}

function onDeleteClick(index) {
  showModal({
    title: '提示',
    content: '确定要移除此项吗?',
    success: ({ confirm }) => {
      if (confirm) {
        const file = files.value[index]
        if (file.task) {
          file.task.abort()
        }
        files.value.splice(index, 1)
      }
    },
  })
}

function uploadFile(files, index) {
  if (state.uploading) return
  const file = files[index]
  if (file.progress === 100) return
  const header = props.header ?? defHeader
  const task = uni.uploadFile({
    url: props.url,
    header: header,
    filePath: file.url,
    name: props.uploadName,
    success: res => {
      const data = JSON.parse(res.data)

      if (data.code == 1) {
        file.serverUrl = data.data.fullurl
        file.progress = 100
        file.error = false
        console.log('uploadFile success', file);
        emit('uploadSuccess', file)
      } else {
        file.progress = 0
        file.error = true
      }
    },
    fail: err => {
      file.progress = 0
      file.error = true
      emit('uploadFail', err)
    },
    complete: res => {
      console.log('uploadFile complete', res)
      state.uploading = false
      file.task = ''

      if (index < files.length - 1) {
        uploadFile(files, index + 1)
      }
    },
  })
  file.task = task
  task.onProgressUpdate(res => {
    const progress = res.progress
    console.log('onProgressUpdate', progress)
    if (progress > 0) {
      file.progress = progress >= 100 ? 99 : progress
    }
  })
}

function getList() {
  return files.value.map(item => {
    return {
      ...item,
      url: item.serverUrl ?? item.url,
    }
  })
}

function getUploadedList() {
  const uploadedList = files.value.filter(item => item.progress == 100)
  return uploadedList.map(item => {
    return {
      ...item,
      url: item.serverUrl ?? item.url,
    }
  })
}

function isAllUploaded(showTip = true) {
  const uploadedList = files.value.filter(item => item.progress == 100)
  const errorList = files.value.filter(item => (item.error = true))
  const isUploaded = uploadedList.length === files.value.length
  if (showTip && !isUploaded) {
    showModal({
      content: '请先等待文件上传',
      showCancel: false,
      success: () => {
        if (errorList.length > 0) {
          uploadFile(errorList, 0)
        }
      },
    })
  }
  return isUploaded
}

function clear() {
  files.value = []
}

defineExpose({ getList, getUploadedList, clear,isAllUploaded,onFileChoosed })
</script>

<style lang="scss" scoped>
.z-upload {
  display: flex;
  flex-wrap: wrap;

  .upload-preview_item {
    position: relative;
    margin: 0 20rpx 20rpx 0;

    image {
      border-radius: 8rpx;
    }

    .upload-preview_item-delete {
      position: absolute;
      top: 4rpx;
      right: 4rpx;
      width: 36rpx;
      height: 36rpx;
      background-color: #f04a4a;
      border-radius: 50%;
      display: flex;
      align-items: center;
      justify-content: center;
      z-index: 99;
    }

    .uploading {
      position: absolute;
      top: 0;
      bottom: 0;
      left: 0;
      right: 0;
      width: 100%;
      height: 100%;
      display: flex;
      flex-direction: column;
      justify-content: flex-end;
      padding: 0 16rpx 20rpx 16rpx;
      background-color: rgba(0, 0, 0, 0.4);
    }
  }

  .upload-btn_add {
    display: flex;
    justify-content: center;
    align-items: center;
    background-color: #f3f5f7;
  }
}</style>
